package ths

import (
	"fmt"
	"gitee.com/gcom/gbox/errors"
	s "gitee.com/gcom/stockdock/core/support"
	"gitee.com/gcom/stockdock/crawler"
	"gitee.com/gcom/stockdock/spider/ths"
	"time"
)

// 更新历史报价
func UpdateStockQuoteHist(ctx s.TracerCtx) error {
	list, ok := getStockList(ctx)
	if !ok || len(list) == 0 {
		return nil
	}

	var count, nodata, total = 0, 0, len(list)
	var codeCh = make(chan string)
	var sem = make(chan int)
	defer close(codeCh)
	onValue := func(req crawler.CrawlRequest, hist []*ths.StockHistThs) {
		if len(hist) == 0 {
			nodata += 1
			s.WriteEvent(ctx, fmt.Sprintf("[%d,%d/%d]股票(%s)无历史报价数据", count, nodata, total, req.(*ths.StockHistCrawlRequest).Code))
		} else {
			count += 1
			s.WriteEvent(ctx, fmt.Sprintf("[%d,%d/%d]获取股票(%s:%s)历史报价%d条", count, nodata, total,
				req.(*ths.StockHistCrawlRequest).Code, hist[0].Name, len(hist)))
			var entities = make([]*ths.StockHistThsEntity, len(hist))
			for i, v := range hist {
				entities[i] = &ths.StockHistThsEntity{StockHistThs: v}
			}

			_, err := ths.UpdateStockQuoteHist(ctx.DB, entities)
			if err != nil {
				s.WriteErrEvent(ctx, "写入数据库失败", err)
				close(sem)
				return
			}
			s.WriteEvent(ctx, fmt.Sprintf("[%d,%d/%d]写入数据库%d条", count, nodata, total, len(entities)))
		}

		sem <- 1 // produce one ticket
	}
	onError := func(req crawler.CrawlRequest, err error) {
		s.WriteErrEvent(ctx, "获取股票历史报价失败", err)
		close(sem)
	}
	onDiscard := func(req crawler.CrawlRequest, err error) {
		nodata += 1
		s.WriteErrEvent(ctx, fmt.Sprintf("[%d,%d/%d](%s)%s", count+1, nodata, total,
			req.(*ths.StockHistCrawlRequest).Code, errors.Cause(err).Error()), err)
		sem <- 1 // produce one ticket
	}

	go func() {
		ths.GetStockQuoteHist(ctx, codeCh, onValue, onError, onDiscard)
	}()

	for i, v := range list {
		codeCh <- v.Code

		select {
		case _, ok := <-sem: // consume 1 ticket
			if !ok {
				err := errors.New(fmt.Sprintf("异常退出，共完成写入股票%d支，无数据%d支，总共%d支", count, nodata, total))
				s.WriteErrEvent(ctx, "异常退出", err)
				return err // task aborted
			}
		}

		if i == len(list)-1 {
			close(sem)
			s.WriteEvent(ctx, fmt.Sprintf("共完成写入股票%d支，无数据%d支，总共%d支", count, nodata, total))
			return nil
		}

		time.Sleep(500 * time.Millisecond)
	}
	return nil
}

func getStockList(ctx s.TracerCtx) (list []*ths.StockCodeAndName, ok bool) {
	var err error
	if ctx.Req != nil { // invoked from http request
		q := &ths.StocksQuery{}
		if !s.ParseAndValidQuery(ctx, q) {
			return nil, false
		}
		if q.Ps == 0 {
			q.Ps = 9999
		}
		list, err = ths.GetStocksCodeAndName(ctx.DB, q)
	} else { // invoked from job
		list, err = ths.GetStocksCodeAndName(ctx.DB, &ths.StocksQuery{Ps: 9999})
	}

	if err != nil {
		s.WriteErrEvent(ctx, "查询股票失败", err)
		return nil, false
	}
	s.WriteEvent(ctx, fmt.Sprintf("共计股票%d支", len(list)))
	return list, true
}
